#pragma once

#include "AsyncLibDef.h"

namespace AsyncLib
{

class CTpFileTask;

class CTpFileManager
{
public:
	CTpFileManager();
	~CTpFileManager();

	void Init();
	void Stop();
	void Exit();
	std::shared_ptr<IFileTask> CreateTask(std::weak_ptr<CTpFileManager> pManager, HANDLE hFile);
	void DeleteTask(PTP_IO pTpIo);
	auto GetSharedLock() { return std::shared_lock(m_mutex); }

private:
	void OnCallback(PTP_IO pTpIo, LPVOID lpOverlapped, UINT nBytesTransferred, DWORD dwError);

	std::shared_mutex m_mutex;
	std::unordered_map<PTP_IO, std::shared_ptr<CTpFileTask>> m_mapTasks;
};

class CTpFileTask : public IFileTask
{
public:
	CTpFileTask(std::weak_ptr<CTpFileManager> pManager, HANDLE hFile, PTP_IO pTpIo) : m_pManager(std::move(pManager)), m_hFile(hFile), m_pTpIo(pTpIo)
	{
	}

	void Read(void *lpBuffer, INT64 nOffset, UINT nBytesToRead, std::function<void(UINT, DWORD)>&& fnCallback) override;
	void Write(const void *lpBuffer, INT64 nOffset, UINT nBytesToWrite, std::function<void(UINT, DWORD)>&& fnCallback) override;
	void Close() override;
	void Cancel(BOOL bExit);
	void DoCallback(LPVOID lpOverlapped, UINT nBytesTransferred, DWORD dwError);

private:
	LPOVERLAPPED AllocCache(INT64 nOffset, std::function<void(UINT, DWORD)>&& fnCallback);
	void ExecuteIo(DWORD (*pfInvoke)(void *, LPOVERLAPPED), void *pfnIo, INT64 nOffset, std::function<void(UINT, DWORD)>&& fnCallback);

	std::weak_ptr<CTpFileManager> m_pManager;
	HANDLE m_hFile;
	PTP_IO m_pTpIo;

	struct
	{
		UINT bUsing;
		OVERLAPPED overlapped;
		std::function<void(UINT, DWORD)> fnCallback;
	} m_caches[2]{};
};

} // namespace AsyncLib
